
import sys
#import cv2
#import numpy as np
import queue
from common.utils import *
import os
import json
import shutil
from defconfig11 import *
from midea_common11.myutils import *

pic_server_url = 'http://192.168.1.104:5000/ai/v1/mdzx_detect'
pic_server_url = 'http://192.168.12.208:5000/ai/v1/mdzx_detect'
prod_url = 'http://10.177.104.86:5000/api'
if os.path.exists('D:/code/git/ywlydd/cstd') or 0:
    pic_server_url = f'http://127.0.0.1:5000/ai/v1/mdzx_detect'
else:
    pic_server_url = f'http://10.177.8.51:5000/ai/v1/mdzx_detect'


confdef['use_thread'] = ['chk_conf', 1, '开启多线程']
confdef['detect_ai'] = ['chk_conf', 0, '开启AI']
confdef['tongji_ai'] = ['chk_conf', 1, '开启统计']
confdef['ng_stop_immediately'] = ['chk_conf', 0, '有NG就立即停止']
confdef['show_false_trigger'] = ['chk_conf', 1, '显示误触发']
confdef['use_max_code_detect_false_trigger'] = ['chk_conf', 0, '用编码器判定误触发']
confdef['min_code'] = ['thd_conf', 2000, 0, 10000, '编码器最小值']
confdef['max_code'] = ['thd_conf', 20000, 0, 40000, '编码器最大值']
#confdef['stop_detect_code'] = ['thd_conf', 5000, 0, 20000, '停止检测编码器值']
confdef['saoma_match_pre'] = ['thd_conf', 10, 0, 100, '条码提前匹配(0.1秒)']
confdef['saoma_match_wait'] = ['thd_conf', 20, 0, 100, '条码延迟匹配(0.1秒)']
confdef['saoma_match_range'] = ['thd_conf', 50, 0, 100, '条码匹配范围(0.1秒)']
confdef['saoma_min_code'] = ['thd_conf', 100, 0, 10000, '条码最小编码器值']
confdef['saoma_max_code'] = ['thd_conf', 5000, 0, 20000, '条码最大编码器值']
confdef['saoma_match_times'] = ['thd_conf', 2, 0, 10, '条码最多匹配次数']
confdef['max_update_pic'] = ['thd_conf', 50, 0, 100, '传图张数限制']
confdef['minrets'] = ['thd_conf', 0, 0, 100, '最少图片数']
confdef['pre_saoma'] = ['chk_conf', 0, '先扫码再检测']
confdef['stopline'] = ['chk_conf', 0, '开启停线']
confdef['scadastopline'] = ['chk_conf', 0, '开启scada停线']
# confdef['upimgs'] = ['chk_conf', 0, '开启传图']
confdef['baojin'] = ['chk_conf', 0, '开启报警']
confdef['post_prod'] = ['chk_conf', 0, '传条码']
confdef['use_scada'] = ['chk_conf', 0, '开启scada']
confdef['img_show_saoma'] = ['chk_conf', 0, '图片显示条码']
confdef['do_hand_saoma'] = ['chk_conf', 0, '启用手动扫码']
confdef['use_pause'] = ['chk_conf', 1, '编码器不动不取图']
confdef['all_img_save'] = ['chk_conf', 0, '保存全部图片']
confdef['not_save_img'] = ['chk_conf', 0, '不保存图片']
confdef['moni_saoma'] = ['chk_conf', 0, '模拟扫码']
confdef['allpass'] = ['chk_conf', 0, '直通']

confdef['stopsleep'] = ['thd_conf', 20, 0, 100, '停止拍照延迟(0.1s)']
confdef['shouhu_timeout'] = ['thd_conf', 20, 0, 100, '守护超时']
confdef['plc_obj_sleep1'] = ['thd_conf', 10, 0, 100, '延迟结束拍照(0.1秒)']
confdef['pic_server_url'] = ['edit_conf', pic_server_url, '传图IP']
confdef['prod_url'] = ['edit_conf', prod_url, '传条码IP']
confdef['saoma_ip'] = ['edit_conf', saoma_ip, '扫码枪IP']
confdef['saoma_port'] = ['edit_conf', str(saoma_port), '扫码枪端口']
confdef['plc_ip'] = ['edit_conf', plc_ip, 'plc_ip']
confdef['plc_port'] = ['edit_conf', '502', 'plc_port']
confdef['start_cam_addr'] = ['edit_conf', '62', '开始拍照信号plc地址']
confdef['end_cam_addr'] = ['edit_conf', '54', '结束拍照信号plc地址']
confdef['code_addr'] = ['edit_conf', '300', '编码器plc地址']
confdef['live_addr'] = ['edit_conf', '31', '心跳包plc地址']
confdef['live_time'] = ['thd_conf', 2, 1, 10, '心跳包间隔时间']
confdef['ng_addr'] = ['edit_conf', '30=1', 'NG信号plc指令']
confdef['reset_addr'] = ['edit_conf', '30=2', '复位plc指令']

confdef['org'] = ['edit_conf', '300052', '工厂代码']
confdef['mes_user'] = ['edit_conf', 'mes_jy_api', 'mes用户名']
confdef['mes_password'] = ['edit_conf', 'Gq3Fv2Ff5P', 'mes密码']
confdef['mes_url'] = ['edit_conf', 'http://10.177.8.193:9989/API/Auth/SignIn', 'mes注册地址']
confdef['mes_upurl'] = ['edit_conf', 'http://10.177.8.193:9989/api/DeviceGeneralTestApi/Test', 'mes上传地址']

confdef['chanxian'] = ['edit_conf', 'PNJ2XT0001', '产线代码']
confdef['workcenter'] = ['edit_conf', 'PNJ2XT0001BTJ', '工作中心']
confdef['classname'] = ['edit_conf', config['classname'], 'classname']
confdef['scadaid'] = ['edit_conf', 'A0607000009220665', '设备A码']
confdef['checkname'] = ['edit_conf', "终端机配件检测" if 'checkname' not in config else config['checkname'], '设备名称']


scada_config1 = {
    "scadaip": "",
    "errorcode":"V4-NZPBD22",
    "terminalCode": "37FW-ZDLJ-5FTH-T5G3-IIAQ",
    "activationCode": "N64Y-DJN6-OK88-MII1-JQXC",
    "scadatype": "ossCertification",
}
for a in scada_config1:
    confdef[a] = ['edit_conf', scada_config1[a], a]

config['progname'] = progname

def get_max_disk():
    import shutil
    disks = 'CDEFG'
    tt = []
    for d in disks:
        try:
            total, used, free = shutil.disk_usage(f'{d}:')
            #logger.info(total, used, free)
            tt.append(total)
        except Exception as e:
            break
    i = np.argmax(tt)
    return disks[i]

def get_out_img_p0():
    D = get_max_disk()
    out_img_p = f'{D}:/data/save/{progname}'
    return out_img_p

out_img_p = get_out_img_p0()
mkdir(out_img_p)

def get_out_img_p():
    d = get_date_stamp()
    outp = f'{out_img_p}/{d}'
    mkdir(outp)
    return outp

def get_outp0():
    d = get_date_stamp()
    outp = f'{out_img_p}/{d}'
    mkdir(outp)
    return outp

def get_outp(index, end):
    d = get_date_stamp()
    aa = 'ng' if end else 'ok'
    outp = f'{out_img_p}/{d}/{aa}'
    outp = f'{out_img_p}/{d}'
    mkdir(outp)
    return outp

def get_outp_hf(index, end):
    d = get_date_stamp()
    # aa = 'ng' if end else 'ok'
    aa = 'ok' if end == 1 else 'ng'
    outp = f'{out_img_p}/{d}/{aa}'
    outp = f'{out_img_p}/{d}/service{index}{aa}'
    mkdir(outp)
    return outp

def get_outp2(index, end, save_dir, product):
    d = get_outp_hf(index, end)
    #outp = f'{d}/{save_dir}_{product}_{aa}'
    # if 'NOREAD' in product.upper() or len(product)<10:
    #     product = f'NOREAD_{save_dir}'
    product = f'{product}_{save_dir}'
    outp = f'{d}/{product}'
    mkdir(outp)
    return outp

def get_outp_noread(index, end, product):
    D = get_max_disk()
    if 'NOREAD' in product and os.path.exists(f'{D}:\\noread'):
        out_img_p1 = f'{D}:\\noread'
        d = get_date_stamp()
        aa = 'NG' if end else 'OK'
        outp = f'{out_img_p1}/{d}/{aa}'
        mkdir(outp)
    else:
        outp = get_outp(index, end)
    return outp

    return outp

config_fn = f'{out_img_p}/config.json'
config_back_fn = f'{out_img_p}/config_back.json'

for x in confdef:
    config[x] = confdef[x][1]


def config_load(fn = config_fn):
    try:
        d = json.load(open(fn, 'r'))
        return d
    except Exception as e:
        pass

    if fn in [config_fn]:
        try:
            d = json.load(open(config_back_fn, 'r'))
            json.dump(d, open(config_fn, 'w'))
            return d
        except Exception as e:
            pass

    return {}

def config_save(fn = config_fn):
    json.dump(config, open(fn, 'w'))
    if fn in [config_fn]:
        json.dump(config, open(config_back_fn, 'w'))

if not os.path.exists(config_fn):
    config_save()

config.update(config_load())
if 1:
    if len(config['saoma_ip'])<2:
        config['saoma_ip'] = saoma_ip
    if len(config['plc_ip'])<2:
        config['plc_ip'] = plc_ip
#logger.info(config)

tongji = {
    'cnt_ng': 0,
    'cnt_ok': 0,
    'cnt_all': 0,
}
datastamp = get_date_stamp()
tongji_fn = f'{out_img_p}/tongji_{datastamp}.json'
if os.path.exists(tongji_fn):
    tongji.update(config_load(tongji_fn))

def load_csv(csv_fn):
    data = [x.strip().split(',') for x in open(csv_fn, 'r').readlines()]
    return data

def save_csv(csv_fn, data):
    data = [','.join([str(y).replace(',', '+') for y in x]) for x in data]
    open(csv_fn, 'w').write('\n'.join(data))
    return data

tongji_table = []
tongji_table_fn = f'{out_img_p}/tongji_table_{progname}_{datastamp}.csv'
if os.path.exists(tongji_table_fn):
    tongji_table = load_csv(tongji_table_fn)

def clear_tongji():
    for x in tongji:
        tongji[x] = 0
    json.dump(tongji, open(tongji_fn, 'w'))

def update_tongji(end):
    tongji['cnt_all'] += 1
    if end:
        tongji['cnt_ng'] += 1
    else:
        tongji['cnt_ok'] += 1

    json.dump(tongji, open(tongji_fn, 'w'))
    return 0

def get_retfns(fns):
    retfns = []
    for sfn in fns:
        retimgfn = sfn
        retimgfn = sfn.replace('_img.jpg', '_ret.jpg')
        retjsonfn = sfn.replace('_img.jpg', '_ret.json')
        for x in [retimgfn, retjsonfn]:
            if os.path.exists(x):
                retfns.append(x)
    
    retfns = list(set(retfns))
    return retfns

def move_ng_images(index, end, product, fns, quexian=''):
    if len(fns)==0:
        return fns
    outp = get_outp_noread(index, end, product)
    s = get_time_stamp()
    dpath = f'{outp}/{s}_{product}_{index}'
    if len(quexian)>0:
        dpath = f'{dpath}_{quexian}'
    retfns = get_retfns(fns)
    out1 = move_fns(retfns, dpath, product)
    out = move_fns(fns, dpath, product)
    out += out1
    return out

def move_ng_images2(index, end, product, fns, quexian=''):
    out = move_ng_images(index, end, product, fns, quexian)
    return out

def get_logger():
    from logging.handlers import TimedRotatingFileHandler
    log_fmt = '%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s'
    formatter = logging.Formatter(log_fmt)
    t = str(time.strftime("%Y-%m-%d-%H-%M-%S", time.localtime()))
    out_p = get_out_img_p()
    fh = TimedRotatingFileHandler(
        filename="%s/run_%s_%s.log" % (out_p, progname, t),
        when="H", interval=1,
        backupCount=72)
    fh.setFormatter(formatter)
    #logging.basicConfig(level=logger.info)
    log = logging.getLogger()
    log.addHandler(fh)
    log.setLevel(logger.info)
    return log

def get_logger():
    try:
        from loguru import logger
    except Exception as e:
        import pip
        pip.main(["install", "--user", "loguru"])
        from loguru import logger
        print(f'pip install loguru')
    out_p = get_out_img_p()
    t = str(time.strftime("%Y-%m-%d-%H-%M-%S", time.localtime()))
    logFile="%s/run_%s_%s.log" % (out_p, progname, t)
    #logger.add(logFile, rotation='200KB', compression='zip', retention="72h")
    logger.add(logFile, rotation='200KB')
    return logger

logger = get_logger()

def del_last_img(out_img_p, min_free = 1000000):
    root_path = out_img_p
    try:
        mkdir(root_path)
        total, used, free = shutil.disk_usage(root_path)
        aa = 100*free/total
        # 10 G
        if free>2024609280:
            return 0
        if aa>3:
            return 0
        li = os.listdir(root_path)
        li.sort()
        logger.info(f'del_last_img: free/total={aa:.3} {root_path}')
        li = li[:5]
        for i in li:
            aa = f'{root_path}/{i}'
            if os.path.isdir(aa):
                if os.path.exists(aa):
                    shutil.rmtree(aa)
            logger.info(f'{aa} 删除成功')
    except Exception as e:
        logger.info(f'del_last_img {str(e)}')
    
    return 1

def plc_check_code1(self, code):
    ss = self
    ss.pre_code = ss.code
    ss.code = code
    ss.code_diff = ss.code - ss.pre_code
    ss.max_code = max(ss.max_code, code)
    is_first = 0
    #logger.info(ss.code, ss.code_diff)
    if ss.code_diff < 1:
        stop_times += 1
    else:
        stop_times = 0
    
    if stop_times>20:
        if ss.pause==0:
            logger.info(f'plc_pause {code} {stop_times}')
        ss.pause = 1
        time.sleep(0.1)
    else:
        ss.pause = 0

    if ss.flag:
        if ss.code_diff<-2000:
            logger.info(f'plc_obj_is_out {ss.code_diff}')
            #ss.qq.put(2)
            time.sleep(config['plc_obj_sleep1']/10)
            ss.flag = 0
    else:
        if (ss.code>0 and ss.code_diff>0) or is_first:
            is_first = 0
            ss.max_code = 0
            logger.info('plc_obj_is_in')
            ss.qq.put(1)
            ss.flag = 1

def do_saoma_match_wait_pre(ss, t1):
    saoma_match_wait = config['saoma_match_wait']/10
    saoma_match_range = config['saoma_match_range']/10
    t2 = t1 + saoma_match_wait
    t1 = min(t2, time.time()-saoma_match_range)
    if t2>t1:
        time.sleep(t2-t1)

    return t1

def do_saoma_match_wait(ss, t1):
    if config['pre_saoma']:
        return t1
    saoma_match_wait = config['saoma_match_wait']/10
    logger.info(f'do_saoma_match_wait {saoma_match_wait}')
    time.sleep(saoma_match_wait)
    t1 = time.time()-0.01
    return t1

def wait_saoma_find_product_range(ss, plc, prems=200):
    saoma_match_wait = config['saoma_match_wait']/10
    t0 = time.time()
    saoma_time_t0 = plc.saoma_time[0]-prems/1000
    product = ss.saoma.find_product_range(saoma_time_t0)
    tt = 0
    while 'NO' in product and plc.saoma_time[1] is None:
        tt = time.time()-t0
        if saoma_match_wait>0 and tt>saoma_match_wait:
            break
        product = ss.saoma.find_product_range(saoma_time_t0)
        time.sleep(0.1)
    logger.info(f'wait_saoma_find_product_range {saoma_match_wait:.2f} {tt:.2f} {product} {saoma_time_t0}')
    return product


def wait_saoma_find_product_range2(ss, plc, prems=200):
    saoma_match_wait = config['saoma_match_wait']/10
    saoma_match_pre = config['saoma_match_pre']/10
    t0 = time.time()
    t1 = time.time()
    tt = t1-t0
    product = 'NO'
    while 'NO' in product:
        t1 = time.time()
        tt = t1-t0
        saoma_time_t0 = plc.saoma_time[0]-saoma_match_pre
        saoma_time_t1 = plc.saoma_time[1]+prems/1000
        if saoma_match_wait>0 and tt>saoma_match_wait and saoma_time_t0<saoma_time_t1:
            break
        saoma_time_t11 = max(saoma_time_t1, t1)
        product = ss.saoma.find_product_range(saoma_time_t0, saoma_time_t11)
        time.sleep(0.1)
    logger.info(f'wait_saoma_find_product_range2 {saoma_match_wait:.2f} {tt:.2f} {product} {saoma_time_t0}')
    return product

def wait_saoma_find_product_range0(ss, saoma_time_t0):
    saoma_match_wait = config['saoma_match_wait']/10
    t0 = time.time()
    t1 = time.time()
    tt = t1-t0
    product = 'NO'
    saoma_match_pre = config['saoma_match_pre']/10
    while 'NO' in product:
        t1 = time.time()
        tt = t1-t0
        if saoma_match_wait>0 and tt>saoma_match_wait:
            break
        product = ss.saoma.find_product_range(saoma_time_t0-saoma_match_pre)
        time.sleep(0.1)
    logger.info(f'wait_saoma_find_product_range0 {saoma_match_wait:.2f} {tt:.2f} {product} {saoma_time_t0}')
    return product

def wait_saoma_find_product_method(ss, t11, method='last'):
    saoma_match_wait = config['saoma_match_wait']/10
    t0 = time.time()
    t1 = time.time()
    tt = t1-t0
    product = 'NO'
    saoma_match_pre = config['saoma_match_pre']/10
    while 'NO' in product:
        t1 = time.time()
        tt = t1-t0
        if saoma_match_wait>0 and tt>saoma_match_wait:
            break
        t111 = t11-saoma_match_pre
        product = ss.saoma.find_product_method(t111, method)
        time.sleep(0.1)
    logger.info(f'wait_saoma_find_product_method {saoma_match_wait:.2f} {tt:.2f} {product} {t1}')
    return product

def do_saoma_match(ss, t1):
    saoma_match_wait = config['saoma_match_wait']/10
    saoma_match_pre = config['saoma_match_pre']/10
    #logger.info(f'do_saoma_match {saoma_match_wait} {saoma_match_pre}')
    #time.sleep(saoma_match_wait)
    product = ss.saoma.find_product(t1-saoma_match_pre)
    return product

def do_saoma_match_wait(saoma, products = []):
    product = 'NOREAD'
    t0 = time.time()
    for i in range(100):
        product = saoma.get_product2(products)
        t1 = time.time()
        tt = t1-t0
        if tt>config['saoma_match_wait']/10 and i>2:
            break
        time.sleep(0.1)

    if len(products)>0:
        product = products[-1]
    return product

def do_end(ss, plc_set_ng, end, code='没扫到码'):
    if end:
        if config['stopline']:
            plc_set_ng(end) # NG

        if config['baojin']:
            logger.info('NG报警')
            baojin(code)

def check_product(ss, plc_set_ng, rets, product, allfns, quexian, end, code, max_code):
    logger.info(f'end {end}')
    logger.info(f'max_code {max_code}')
    logger.info(f'product {product}')
    aa = tongji['cnt_ok']/max(1, tongji['cnt_all'])
    show_noread = 1
    canguan = 0
    D = get_max_disk()
    if os.path.exists(f'{D}:\\canguan'):
        canguan = 1

    if 'NO' in product:
        if config['detect_ai'] and (tongji['cnt_all']<10 or aa<0.95) and 0:
            quexian.append('误触发')
            code = '误触发' 
            end = 0
            max_code = 0
        else:
            quexian.append('没扫到码')
            code = '没扫到码' 
            end = 1

        if os.path.exists(f'{D}:\\noread'):
            show_noread = 0

    if config['use_max_code_detect_false_trigger']:
        if max_code<config['min_code'] or max_code>config['max_code']:
            end = 0
            quexian = ['误触发']

    if config['detect_ai']:
        cnt_all = tongji['cnt_all']
        cnt_ng = tongji['cnt_ng']
        cnt_ok = tongji['cnt_ok']
        a = cnt_ok*100/max(cnt_all,1)
        if a<99.1:
            quexian = []
            end = 0

    if end:
        if config['progname'] not in 'baoya mingpai guanao dashangbiao'.split():
            if len(rets)<config['minrets']:
                quexian = ['误触发']
                end = 0

    if canguan:
        quexian = []
        end = 0

    if show_noread:
        do_end(ss, plc_set_ng, end, code)

    quexian = ','.join(list(set(quexian)))
    logger.info(f'quexian: {quexian}')
    if ss.ss is not None:
        if ('误触发' not in quexian or config['show_false_trigger']) and show_noread:
            ss.ss.changeSignal.emit([end, product, quexian, max_code]) # 发送到ui

    allfns = move_ng_images2(0, end, product, allfns, quexian)
    if ss.upimgs is not None:
        ss.upimgs.put(allfns, ss.saoma, config['progname'], end)

    return end, code

def do_end2(ss, plc_set_ng, end, code='没扫到码'):
    if config['stopline']:
        plc_set_ng(end) # NG

    if config['baojin']:
        logger.info('NG报警')
        baojin(code)

def check_product2(ss, plc_set_ng, rets, product, allfns, quexian, end, code, max_code, save_dir):
    logger.info(f'end {end}')
    logger.info(f'max_code {max_code}')
    logger.info(f'product {product}')
    aa = tongji['cnt_ok']/max(1, tongji['cnt_all'])
    show_noread = 1
    canguan = 0
    D = get_max_disk()
    if os.path.exists(f'{D}:\\canguan'):
        canguan = 1

    if 'NO' in product:
        if config['detect_ai'] and (tongji['cnt_all']<10 or aa<0.95):
            quexian.append('误触发')
            code = '误触发' 
            end = 0
            max_code = 0
        else:
            quexian.append('没扫到码')
            code = '没扫到码' 
            end = 1

        if os.path.exists(f'{D}:\\noread'):
            show_noread = 0

    if config['use_max_code_detect_false_trigger']:
        if max_code<config['min_code'] or max_code>config['max_code']:
            end = 0
            quexian = ['误触发']

    if config['detect_ai'] and 0:
        cnt_all = tongji['cnt_all']
        cnt_ng = tongji['cnt_ng']
        cnt_ok = tongji['cnt_ok']
        a = cnt_ok*100/max(cnt_all,1)
        if a<99.1:
            quexian = []
            end = 0

    if end:
        if config['progname'] not in 'baoya mingpai guanao dashangbiao'.split():
            if len(rets)<config['minrets']:
                quexian = ['误触发']
                end = 0

    if canguan:
        quexian = []
        end = 0

    do_end2(ss, plc_set_ng, end, code)

    quexian = ','.join(list(set(quexian)))
    if len(allfns)>0:
        if 0:
            outdir = get_outp(end, product)
            #allfns = move_ng_images2(0, end, product, allfns, quexian)
            srcdir = os.path.split(allfns[0])[0]
            srcdir = os.path.split(srcdir)[0]
            try:
                os.rename(srcdir, outdir)
                #shutil.move(srcdir, outdir)
            except Exception as e:
                #i = random.randint(0, 1000) % 2
                logger.info(f'rename err {str(e)}')
                pass
            
    if ss.upimgs is not None:
        ss.upimgs.put(allfns, ss.saoma, config['progname'], end)

    logger.info(f'quexian: {quexian}')
    if ss.ss is not None:
        if ('误触发' not in quexian or config['show_false_trigger']) and show_noread:
            ss.ss.changeSignal.emit([end, product, quexian, max_code])

    return end, code

def do_end_hf(ss, plc_set_ng, end, code='没扫到码'):
    if end:
        if config['stopline']:
            plc_set_ng(end) # NG

        if config['baojin']:
            logger.info('NG报警')
            baojin(code)

def check_product_hf(ss, plc_set_ng, rets, product, allfns, quexian, end, code, max_code, save_dir):
    logger.info(f'end {end}')
    logger.info(f'max_code {max_code}')
    logger.info(f'product {product}')
    aa = tongji['cnt_ok']/max(1, tongji['cnt_all'])
    show_noread = 1
    canguan = 0
    D = get_max_disk()
    if os.path.exists(f'{D}:\\canguan'):
        canguan = 1

    if 'NO' in product:
        if config['detect_ai']:
            return end, code
        else:
            quexian.append('没扫到码')
            code = '没扫到码' 
            end = 1
    elif config['allpass']:
        quexian = []
        code = '' 
        end = 0
        
    #update_tongji_hf()

    if config['use_max_code_detect_false_trigger']:
        if max_code<config['min_code'] or max_code>config['max_code']:
            end = 0
            quexian = ['误触发']

    if config['detect_ai']:
        cnt_all = tongji['cnt_all']
        cnt_ng = tongji['cnt_ng']
        cnt_ok = tongji['cnt_ok']
        a = cnt_ok*100/max(cnt_all,1)
        if a<99.1:
            quexian = []
            end = 0

    if end:
        if config['progname'] not in 'baoya mingpai guanao dashangbiao'.split():
            if len(rets)<config['minrets']:
                quexian = ['误触发']
                end = 0

    if canguan:
        quexian = []
        end = 0

    do_end_hf(ss, plc_set_ng, end, code)

    quexian = ','.join(list(set(quexian)))
    if len(allfns)>0:
        if 0:
            outdir = get_outp(end, product)
            #allfns = move_ng_images2(0, end, product, allfns, quexian)
            srcdir = os.path.split(allfns[0])[0]
            srcdir = os.path.split(srcdir)[0]
            try:
                os.rename(srcdir, outdir)
                #shutil.move(srcdir, outdir)
            except Exception as e:
                #i = random.randint(0, 1000) % 2
                logger.info(f'rename err {str(e)}')
                pass
            
    if ss.upimgs is not None:
        ss.upimgs.put(allfns, ss.saoma, config['progname'], end)

    dirpa = ''
    if len(allfns)>0:
        dirpa = os.path.split(allfns[0])[0]
    tongji_table.append([end, product, quexian, max_code, dirpa])
    save_csv(tongji_table_fn, tongji_table)

    logger.info(f'quexian: {quexian}')
    if ss.ss is not None:
        if ('误触发' not in quexian or config['show_false_trigger']) and show_noread:
            ss.ss.changeSignal.emit([end, product, quexian, max_code])

    return end, code


def action_end(index, ss, numpy_image, im1, ccc, end, ends, save_img, product, max_code):  # numoy原图，im检测图，ccc结果
    if config['img_show_saoma']:
        cv2.putText(im1, f'{product}', (100, 300),
                    cv2.FONT_HERSHEY_COMPLEX_SMALL, 2, (0, 0, 255), 3)

    if ss.ss is not None:
        ss.ss.showSignal.emit([index, numpy_image, im1, end, ends, save_img])
    else:
        imshow(f'cam{index} action_end', im1, 10)

    fns = []
    if config['all_img_save']:
        save_img = 1
    if save_img:
        outp = get_outp(index, end)
        s = get_time_stamp()
        ret = 'NG' if end else 'OK'
        pp = f'{outp}/img_{index}_{s}_{product}_{max_code}_{ret}'
        fn = f'{pp}_img.jpg'
        cv_imwrite(fn, numpy_image)
        fns.append(fn)

        #cv_imwrite(fn, numpy_image)
        if im1 is not None:
            fn = f'{pp}_ret.jpg'
            #logger.info(f'save {fn}')
            cv_imwrite(fn, im1)
            fns.append(fn)
            fn = f'{pp}_ret.json'
            json.dump(ccc, open(fn, 'w'))
        del_last_img(out_img_p)

    return fns


def action_end2(index, ss, numpy_image, im1, ccc, end, ends, save_img, product, save_dir):  # numoy原图，im检测图，ccc结果
    cv2.putText(im1, f'{product}', (100, 300),
                cv2.FONT_HERSHEY_COMPLEX_SMALL, 2, (0, 0, 255), 3)
    # 1 界面显示
    if ss.ss is not None:
        # ss.ss.showSignal.emit([index, numpy_image, im1, end, ends, save_img])
        ss.ss.showSignal.emit([index - 1, numpy_image, im1, end, ends, save_img])
    else:
        imshow(f'cam{index} action_end', im1, 10)

    # 2 保存原图，框图，json
    fns = []
    if save_img or config['all_img_save']:
        outp = get_outp2(index, end, save_dir, product)
        s = get_time_stamp()
        ret = 'NG' if end else 'OK'
        # name = camnames[index] # user 20240107
        pp = f'{outp}/img_{index}_{s}_{product}_{ret}'
        fn = f'{pp}_img.jpg'
        cv_imwrite(fn, numpy_image) # 原图
        fns.append(fn)

        #cv_imwrite(fn, numpy_image)
        if im1 is not None:
            fn = f'{pp}_ret.jpg'
            #print(f'save {fn}')
            cv_imwrite(fn, im1)
            print(f'save to  {fn} ')
            fns.append(fn)
            fn = f'{pp}_ret.json'
            json.dump(ccc, open(fn, 'w'))
        del_last_img(out_img_p)

    return fns


def update_tongji_hf():
    #pa = get_out_img_p()
    #cnt_ng
    tt = [0, 0]
    for x in [0, 1]: 
        pa1 = get_outp_hf(0, x)
        li = [a for a in os.listdir(pa1) if os.path.isdir(f'{pa1}/{a}')]
        tt[x] = len(li)
    tongji['cnt_ng'] = tt[1]
    tongji['cnt_ok'] = tt[0]
    tongji['cnt_all'] = tt[0]+tt[1]
    return 0

#print('111,222;333'.replace(';',',').split(','))

def do_plc_cmd(plc_set16_fun, s):
    try:
        logger.info(f'do_plc_cmd {s}')
        xx = s.replace(';',',').split(',')
        for x in xx:
            if '=' in x:
                tt = [int(a) for a in x.split('=')]
                addr,value = tt[:2]
                plc_set16_fun(addr, value)
            elif 'ms' in x:
                a = int(x.split('m')[0])/1000
                time.sleep(a)
            elif 's' in x:
                a = int(x.split('s')[0])
                time.sleep(a)

    except Exception as e:
        logger.info(f'do_plc_cmd err: :{str(e)}')
    return 0


if __name__ == "__main__":
    pa = 'D:/data/220329竹筷/pic'
    pa = 'D:/data/220329竹筷'
    #update_tongji_hf()
    #del_last_img()
    # cv2.destroyAllWindows()
